﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;

namespace StorySmith
{
    public static class Behavior
    {

        public static GivenWrapper Given(this StoryWrapper story, IGiven given)
        {

            DisplayGiven(given, "");
            given.DoWork();
            return new GivenWrapper();
        }

        public static GivenWrapper Given(this StoryWrapper story, IGiven given, string additionalInfo)
        {
            DisplayGiven(given, additionalInfo);
            given.DoWork();
            return new GivenWrapper();
        }


        public static GivenWrapper Given(this StoryWrapper story, object given)
        {
            DisplayGiven(given, String.Empty);
            InvokeMethodWithSetupAttribute(given);
            return new GivenWrapper();
        }

        public static GivenWrapper Given(this StoryWrapper story, object given,string additionalInfo)
        {

            DisplayGiven(given, additionalInfo);
            InvokeMethodWithSetupAttribute(given);
            return new GivenWrapper();
        }

        public static GivenWrapper Given(this StoryWrapper story, string additionalInfo)
        {

            DisplayGiven(null, additionalInfo);
            return new GivenWrapper();
        }

        private static void DisplayGiven(object given, string additionalInfo)
        {
            string typeName = given==null?"":given.GetType().Name;
            Console.WriteLine("Given " + typeName + additionalInfo);
        }

        private static void DisplayAnd(object given, string additionalInfo)
        {
            Type type = given.GetType();
            Console.WriteLine(" and " + type.Name + additionalInfo);
        }

        private static void InvokeMethodWithSetupAttribute(object given)
        {
            //iterate to find [SetUp] and execute
            Type type = given.GetType();
            MethodInfo[] mi = type.GetMethods();
            foreach (MethodInfo method in mi)
            {
                foreach (Attribute attribute in method.GetCustomAttributes(false))
                {

                    if (attribute.GetType().Name.ToLower() == "setupattribute")
                    {
                        method.Invoke(given, null);
                    }
                }

            }
        }



        public static GivenWrapper And(this GivenWrapper extendedObject, object given)
        {
            DisplayAnd(given,"");
            InvokeMethodWithSetupAttribute(given);
            return new GivenWrapper();
        }
        public static GivenWrapper And(this GivenWrapper extendedObject, object given, string additionalInfo)
        {
             DisplayAnd(given,additionalInfo);
             InvokeMethodWithSetupAttribute(given);
            return new GivenWrapper();
        }
        public static GivenWrapper And(this GivenWrapper extendedObject, IGiven given)
        {

            given.DoWork();
            DisplayAnd(given, "");
            return new GivenWrapper();
        }
        public static GivenWrapper And(this GivenWrapper extendedObject, IGiven given, string additionalInfo)
        {

            given.DoWork();
            DisplayAnd(given, additionalInfo);
            return new GivenWrapper();
        }
        
        public static void Should(Action action)
        {
            // get call stack
            StackTrace stackTrace = new StackTrace();

            string when = stackTrace.GetFrame(1).GetMethod().DeclaringType.Name.Replace("_"," ");
            
            // get calling method name
            string then= stackTrace.GetFrame(1).GetMethod().Name.Replace("_"," ");
            
            Console.WriteLine(String.Format("\t{0}:\n\t\t{1}", when, then));
                                                       
            action.Invoke();
        }

        public static StoryWrapper For(IStory story)
        {

            string description = String.Format("As a {0}, I want to {1} ,\n so that {2}",
                                                story.AsA, story.IWantTo, story.SoThat);
            Console.WriteLine(description);

            return new StoryWrapper();
        }



       
    }
}
